#include "fighting.hpp"
#include "tjm_action.hpp"
#include "types.h"
#include <cstdint>
#include <ctime>

/*
 * 1. I need the fastest move, but I can not waiting for it.
 * 2. I do not konw how huge loss when I close something.
 */

namespace Trade {

price_t
Incomming::get_max_rvn_delta() const noexcept
{
  return (max_win_rvn - rvn_single);
}

void
Incomming::update_revenue(const price_t hp_now,
                          const dir_t dir,
                          const price_t avg_px,
                          const int8_t amount,
                          const time_t tm,
                          const int64_t idx) noexcept
{
  hpx_average = avg_px;
  if (amount > 0) {
    if (!idx_rvn_max && !idx_rvn_min) {
      rvn_max = rvn_min = rvn_single;
      idx_rvn_max = idx_rvn_min = idx;
      tm_rvn_max = tm_rvn_min = tm;
    }

    rvn_single = (hp_now - avg_px) * dir - 5;
    if (rvn_single > 0)
      ++count_win;
    else
      ++count_loss;
    rvn_total = rvn_single * amount;

    if (rvn_single > rvn_max) {
      rvn_max = rvn_single;
      hp_rvn_max = hp_now;
      tm_rvn_max = tm;
      idx_rvn_max = idx;

      if (rvn_single > 0) {
        max_win_rvn = rvn_single;
      }
    }

    if (rvn_single < rvn_min) {
      rvn_min = rvn_single;
      hp_rvn_min = hp_now;
      tm_rvn_min = tm;
      idx_rvn_min = idx;

      if (rvn_single < 0) {
        max_loss_rvn = rvn_single;
      }
    }
  }
}

price_t
Incomming::max_slope() const noexcept
{
  if (tm_rvn_min > tm_rvn_max) {
    return (rvn_max - rvn_min) / (tm_rvn_min - tm_rvn_max);
  } else {
    return (rvn_max - rvn_min) / (tm_rvn_max - tm_rvn_min);
  }
}

price_t
Incomming::get_amp() const noexcept
{
  return (rvn_max - rvn_min - HOP_FOR_COMMISION);
}

const price_t THE_BIG_REVERSE = 25;
const price_t THE_MIN_BIG_REVENUE = 80;
bool
Incomming::big_needle_changed(const price_t hp_now, const time_t ts) noexcept
{
  dir_t d_reverse = 0;
  bool b_needle = false, b_big_needle = false;
  if (std::abs(rvn_max - rvn_single) > THE_BIG_REVERSE) {
    d_reverse = sign(hp_rvn_min - hp_rvn_max);
  }

  b_needle = (d_reverse && (ts - tm_rvn_max < 300)); // 5 minutes
  b_big_needle =                                     // 5 < time <= 15
    (d_reverse && (ts - tm_rvn_max > 300) && (ts - tm_rvn_max <= 60 * 15));

  dir_t db = 0;
  if (d_reverse && (b_needle || b_big_needle)) {
    db = d_reverse;
  }

  bool changed = false;
  if (db != d_big_reverse) {
    changed = true;
    d_big_reverse = db;

    im_big_needle = (db != 0);
  }

  return changed;
}

void
Incomming::copy(const Incomming* in) noexcept
{
  memcpy((void*)(this), in, sizeof(Incomming));
}

void
Incomming::reset() noexcept
{
  memset((void*)(this), 0, sizeof(Incomming));
}
price_t
Incomming::try_rvn(const price_t hpx, const dir_t d) const noexcept
{
  return ((hpx - hpx_average) * d);
}

price_t
Incomming::get_shrink() const noexcept
{
  return rvn_min - rvn_max + HOP_FOR_COMMISION;
}

price_t
Incomming::get_rvn_delta() const noexcept
{
  return (0);
}

dir_t
Incomming::get_dir_rvn_delta(const dir_t md) const noexcept
{
  dir_t d = 0;
  price_t delta = 0;
  if (tm_rvn_max > tm_rvn_min) {
    delta = rvn_single - rvn_max;
    dir_t a = std::abs(delta);
    d = (a > 3.5) ? -md : md;
  } else if (tm_rvn_max < tm_rvn_min) {
    delta = rvn_single - rvn_min;
    dir_t a = std::abs(delta);
    d = (a > 3.5) ? md : -md;
  }

  return d;
}

bool
Incomming::big_reverse_changed(const price_t hp_now, const time_t ts) noexcept
{
  bool br = false;
  if ((rvn_max - rvn_min > THE_MIN_BIG_REVENUE) &&
      (rvn_max - rvn_single > THE_BIG_REVERSE)) {
    br = true;
  }

  bool changed = false;
  if (br != im_big_reverse) {
    changed = true;
    im_big_reverse = br;
  }
  return changed;
}

price_t
Incomming::get_good_for_win(const dir_t d) const noexcept
{
  if (rvn_max > 10) {
    return hp_rvn_max - rvn_max * .2 * d;
  } else {
    price_t hp = (rvn_max - rvn_min) * .8;
    if (hp < 0)
      hp = 8;
    return hp_rvn_min + d * hp;
  }
}

price_t
Incomming::get_stop_loss(const case_t cno) const noexcept
{
  if (cno == CASE_BIG_REVERSE_OCCURED) {
  }

  return CLOSE_REVENUE_FOOL_FORCED;
}

price_t
Incomming::get_stop_win(const case_t cno) const noexcept
{
  if (cno == CASE_BIG_REVERSE_OCCURED) {
    price_t hp = (rvn_max - rvn_min) * ppi + rvn_min;
    if (hp < 0)
      hp = rvn_max * ppi;
    return hp;
  }

  return (0);
}

price_t
Incomming::get_loss_rate() const noexcept
{
  return price_t(count_loss) / price_t(count_win + count_loss);
}

Pricing::Pricing()  noexcept
{
  clear();
}

void
Pricing::cal_average() noexcept
{
  if (amount > 0) {
    price_t s = 0;
    for (int i = 0; i < amount; ++i) {
      s += price[i];
    }
    average = s / amount;
  } else {
    average = 0;
  }
}

void
Pricing::push_back(const price_t p) noexcept
{
  if (amount < LENGTH) {
    price[amount++] = p;
  }
  cal_average();
}

void
Pricing::clear() noexcept
{
  memset((void*)(price), 0, sizeof(price_t) * LENGTH);
  amount = 0;
  average = 0;
}

void
Pricing::pop_back() noexcept
{
  if (amount > 0) {
    price[amount--] = 0;
    cal_average();
  }
}

void
Pricing::push(const price_t p1,
                    const price_t p2,
                    const price_t p3,
                    const price_t p4) noexcept
{
  amount = 0;
  if (p1 > 0)
    price[amount++] = p1;
  if (p2 > 0 &&  amount < LENGTH)
    price[amount++] = p2;
  if (p3 > 0 &&  amount < LENGTH)
    price[amount++] = p3;
  if (p4 > 0 &&  amount < LENGTH)
    price[amount++] = p4;
  cal_average();
}

void
Pricing::print(const price_t hp_now) const noexcept {
  if (amount > 0) {
   printf("AVG %.02f, ", average);
   for (int i = 0; i < amount; i++) {
    printf("%.02f ", price[i]);
   }
   printf("\n");
  }
}

} // namespace trade
